package com.qf.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.entity.TlUser;
import com.qf.utils.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class QfUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    RedisTemplate redisTemplate;
    AuthenticationManager authenticationManager;

    String uuid;

    public QfUsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager, RedisTemplate redisTemplate)
    {
        this.redisTemplate=redisTemplate;
        this.authenticationManager=authenticationManager;
        //设置登录的请求接口
        this.setPostOnly(true);
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login", "POST"));
    }    //认证的过程：过程/成功/失败
    //重写认证过程
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            uuid= UUID.randomUUID().toString();
        if (uuid!=null) {
            //判断错误次数是否超了(是否存在/次数是3)
            Object o = redisTemplate.opsForValue().get(RedisKey.LOGIN_ADMIN_FAILURE + uuid);
            //o不存在，正常执行认证；
            //o存在，但值小于3，正常执行认证
            //o存在，且值==3，抛出异处理
            if (o != null && ((Integer) o) == 3) {
                //账号是锁定的状态，但是后续不允许，处理方案，延续计时
                redisTemplate.expire(RedisKey.LOGIN_ADMIN_FAILURE + uuid, 1, TimeUnit.MINUTES);
                //报错
                throw new RuntimeException("错误次数过多，请稍后再试");
            }
            //正常可认证的操作
            //读入要认证的相关参数
            ObjectMapper objectMapper = new ObjectMapper(); //读入参数
            TlUser user = objectMapper.readValue(request.getInputStream(), TlUser.class);//封装
            //将数据交给security进行认证：认证管理器
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), user.getUserPass(), new ArrayList<>());
            return authenticationManager.authenticate(usernamePasswordAuthenticationToken);
        }
            //认证-security
        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
        return super.attemptAuthentication(request, response);
    }

    //认证成功后的业务
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        //从认证结果中获得信息
        SecurityUser securityUser=(SecurityUser) authResult.getPrincipal();//获得用户名--security处理后的用户

        String username = securityUser.getUsername();
        String password = securityUser.getPassword();
        //记录--redis中记录管理员的登录状态
        String jwt = TokenTools.create(username,password);//===================生成jwt的方式===============

        redisTemplate.boundValueOps(RedisKey.LOGIN_USER+uuid).set(jwt);
        //super.successfulAuthentication(request, response, chain, authResult);
        //返回到前端，ResultMsg
        ResponseUtils.response(response, ResultMsg.SUCCESS(uuid));//token
    }

    //认证失败后的业务
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        //super.unsuccessfulAuthentication(request, response, failed);
        //记录错误次数  login_admin_failure:
        Long increment = redisTemplate.boundValueOps(RedisKey.LOGIN_ADMIN_FAILURE + uuid).increment();//默认从1开始增加
        if(increment>=3){
            //将账号锁定
            redisTemplate.expire(RedisKey.LOGIN_ADMIN_FAILURE+uuid,1, TimeUnit.MINUTES);
        }
        ResponseUtils.response(response, ResultMsg.ERROR(999,"security认证失败!"));//token
    }
}
